package com.totem.base.util;

import com.totem.base.constants.CacheConstants;
import com.totem.base.constants.CommonConstants;
import com.totem.base.constants.ErrorCode;
import com.totem.base.exception.BaseException;
import org.apache.commons.lang3.StringUtils;
import org.springframework.http.HttpHeaders;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.util.AntPathMatcher;
import org.springframework.util.MultiValueMap;

import javax.servlet.http.HttpServletRequest;
import java.util.Optional;
import java.util.Set;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class RequestUtil {
    private static final Pattern authorizationPattern = Pattern.compile("^bearer (?<token>[a-zA-Z0-9-._~+/]+)=*$", 2);

    private static final AntPathMatcher checkIgnoresMatch = new AntPathMatcher();
    private static final AntPathMatcher logIgnoresMatch = new AntPathMatcher();
    private static final AntPathMatcher downFileIgnoresMatch = new AntPathMatcher();
    private static final Set<String> checkIgnores = Set.of("/", "/error","/favicon.ico",
            "/webjars/**","/actuator/**"
    );
    private static final Set<String> logIgnores = Set.of("/", "/favicon.ico",
            "/webjars/**","/actuator/**"
    );

    private static final Set<String> uploadFileIgnores = Set.of(
            "/**/*upload*", "/**/*import*", "**/*upload*", "**/*import*"
    );

    private static final Set<String> downFileIgnores = Set.of(
            "/**/*download*","/**/*export*", "**/*download*","**/*export*"
    );

    private RequestUtil(){}

    public static boolean notCheck(String requestPath) {
        return checkIgnores.stream().anyMatch(e->checkIgnoresMatch.match(e, requestPath));
    }

    public static boolean noLog(String requestPath){
        return logIgnores.stream().anyMatch(e->logIgnoresMatch.match(e, requestPath));
    }

    public static boolean ignoreDownFile(String requestPath) {
        return downFileIgnores.stream().anyMatch(e->downFileIgnoresMatch.match(e, requestPath));
    }

    public static String requestTime(HttpServletRequest request){
        String requestTime = request.getHeader(CommonConstants.CLIENT_REQUEST_TIME);
        if(StringUtils.isBlank(requestTime)){
            requestTime = request.getHeader(CommonConstants.REQUEST_TO_GATEWAY);
        }
        return requestTime;
    }

    public static String getRelativePath(HttpServletRequest request){
        String contextPath = request.getContextPath();
        String requestUri = request.getRequestURI();
        return requestUri.substring(contextPath.length());
    }

    public static String getRelativePath(ServerHttpRequest request){
        return request.getURI().getPath();
    }

    public static String resolve(HttpServletRequest request){
        Optional<String> optToken = resolveFromAuthorizationHeader(request);
        if(optToken.isEmpty()){
            optToken = resolveFromQueryParams(request);
        }
        return optToken.orElse("");
    }

    public static String resolve(ServerHttpRequest request) {
        Optional<String> optToken = resolveFromAuthorizationHeader(request.getHeaders());
        if (optToken.isEmpty()){
            optToken = resolveFromQueryParams(request.getQueryParams());
        }
        return optToken.orElse("");
    }

    private static Optional<String> resolveFromAuthorizationHeader(HttpServletRequest request) {
        String authorization = request.getHeader(CacheConstants.KEY_TOKEN);
        return doResolveFromHeader(authorization);
    }

    private static Optional<String> resolveFromAuthorizationHeader(HttpHeaders httpHeaders){
        String authorization = httpHeaders.getFirst(CacheConstants.KEY_TOKEN);
        return doResolveFromHeader(authorization);
    }

    private static Optional<String> doResolveFromHeader(String authorization){
        Optional<String> opt = Optional.empty();
        if (StringUtils.startsWithIgnoreCase(authorization, "bearer")) {
            Matcher matcher = authorizationPattern.matcher(authorization);
            if (!matcher.matches()) {
                throw new BaseException(ErrorCode.UNAUTHORIZED.getCode(), "token格式错误");
            } else {
                return Optional.ofNullable(matcher.group("token"));
            }
        } else {
            return opt;
        }
    }

    private static Optional<String> resolveFromQueryParams(MultiValueMap<String, String> queryParams){
        return Optional.ofNullable(queryParams.getFirst("token"));
    }

    private static Optional<String> resolveFromQueryParams(HttpServletRequest request) {
        String[] values = request.getParameterValues("token");
        if (values != null && values.length != 0) {
            if (values.length == 1) {
                return Optional.ofNullable(values[0]);
            }
        }
        return Optional.empty();
    }
}
